Diese Seite behandelt Beispiele auf mittlerem Niveau für die PySide-GUI-Verwaltung (die begleitenden Seiten behandeln Aspekte, die weniger oder weiter fortgeschritten sind, PySide-Beispiele für Anfänger und PySide-Beispiele für Fortgeschrittene). Auf dieser Seite wird ein Beispielprogramm verwendet, um die verschiedenen PySide-Themen abzudecken. Es ist beabsichtigt, etwas funktionalen PySide-Code vorzustellen, so dass jeder, der PySide benutzen muss, den entsprechenden Abschnitt herauskopieren, modifizieren und an seine eigenen Zwecke anpassen kann.
Das "Beispielprogramm" ist eigentlich eine große Klassendefinition, die Festlegung einer PySide-GUI-Klasse, und hat über 150 Zeilen Code (einschließlich Kommentaren). Die Klasse bzw. ihr Verhalten dienen keinem funktionalen Zweck, der einzige Zweck ist, mögliche GUI-Aktionen darzustellen und etwas Code zu präsentieren, der hoffentlich von anderen FreeCAD-Benutzern verwendet werden kann.
Die Klassendefinition und die geringe Anzahl von Codezeilen, die aufgerufen werden, werden in der Reihenfolge beschrieben, in der sie in der Datei vorkommen. Diese Reihenfolge basiert auf dem Bildschirmlayout, das ziemlich willkürlich ist und ausschließlich der Darstellung von Anzeigeelementen dient. Dies ist der modale GUI-Bildschirm, den die PySide-Klasse erzeugt:
Das Meiste des Restes dieses Abschnitts beschreibt den Inhalt der Klassendefinition, die am Ende dieses Abschnitts erscheint. Zuerst behandeln wir die Vereinbarungselemente (declarative elements), die festlegen wie die Dinge funktionieren und wie die Benutzeroberfläche (GUI) aufgebaut ist; dann behandeln wir die operativen Abschnitte (d.h. den Code, der ausgeführt wird, wenn Interaktionen des Benutzers erfolgen). Dieses Fenster basiert auf der Klasse QDialog und ist daher modal, was bedeutet, dass keine Aktivitäten außerhalb des Fensters erfolgen können, solange es geöffnet ist.
Die zwingend erforderliche Import-Anweisung:
from PySide import QtGui, QtCore
Diese wird am besten am Anfang der Python-Datei eingesetzt.
class ExampleModalGuiClass(QtGui.QDialog):
""""""
def __init__(self):
super(ExampleModalGuiClass, self).__init__()
self.initUI()
def initUI(self):
Dieser Code wird am besten Wort für Wort kopiert und dann angepasst. Die Idee dieses Codes ist, dass wir eine Unterklasse (sub-class) der Klasse QDialog von PySide erstellen. Beim Übernehmen des Codes wird der Name der Klasse zu "ExampleModalGuiClass" geändert; dabei ist darauf zu achten ihn an beiden Stellen zu ändern (im Beispiel in Zeile 1 und 4).
self.result = userCancelled
Dieser ist nicht zwingend erforderlich sondern eher guter Programmierstil, er gibt einen Return-Status für das Fenster vor, der vorhandenen ist, egal, was der Anwender ausführt. Dieser kann im Verlauf des Codes durch den Python-Code zum Anzeigen von verschiedenen Optionen, die der Benutzer ausgewählt haben könnte, angepasst werden.
# create our window
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 400, 350)
self.setWindowTitle("Our Example Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
Da die Bildschirmabmessungen von der oberen linken Ecke aus gemessen werden, beziehen sich die Werte in der dritten Zeile auf:
Der Titel des Fensters ist festgelegt, und die letzte Zeile bedeutet lediglich, dass dieses Fenster niemals von einem anderen Fenster verdeckt wird. Wenn dies nicht gewünscht ist, setzt man einfach ein Python-Kommentarzeichen ('#') als erstes Zeichen der Zeile.
# create some Labels
self.label1 = QtGui.QLabel(" ", self)
self.label1.setFont('Courier') # set to a non-proportional font
self.label1.move(20, 20)
self.label2 = QtGui.QLabel("sample string number two", self)
self.label2.move(20, 70)
self.label3 = QtGui.QLabel(" ", self)
self.label3.setFont('Courier') # set to a non-proportional font
self.label3.move(20, 120)
self.label4 = QtGui.QLabel("can you see this?", self)
self.label4.move(20, 170)
In PySide dienen Labels zwei Zwecken: als statische Labels (wie der Name schon sagt) sowie als schreibgeschützte (d. h. nur zur Anzeige bestimmte) Textfelder. So können sowohl unveränderliche Anweisungen an den Benutzer wie "Drücken Sie nicht den roten Knopf" als auch dynamische Berechnungsergebnisse wie "42" an den Benutzer übermittelt werden. In der zweiten Zeile wird ein Label deklariert und sein Anfangswert festgelegt (in diesem Fall leer). Die dritte Zeile legt die Schriftart fest. Es kann jede beliebige Schriftart (auf dem System) angegeben werden. Wenn keine Angabe erfolgt, wird die Standardschriftart verwendet. In diesem Fall wird eine nicht proportionale Schriftart angegeben. Das Label wird an seine Position im Fenster verschoben – seine Koordinaten geben seine Position in Bezug auf das Fenster (nicht den Bildschirm) an.
# checkboxes
self.checkbox1 = QtGui.QCheckBox("Left side", self)
self.checkbox1.clicked.connect(self.onCheckbox1)
#self.checkbox1.toggle() # will set an initial value if executed
self.checkbox1.move(210,10)
#
self.checkbox2 = QtGui.QCheckBox("Right side", self)
self.checkbox2.clicked.connect(self.onCheckbox2)
self.checkbox2.move(210,30)
Checkboxen können in beliebiger Kombination aktiviert und deaktiviert werden (im Gegensatz zu Radiobuttons). Zeile 2 deklariert eine Checkbox und legt ihren Anfangswert fest. Zeile 3 gibt an, welche Methode ausgeführt wird, wenn die Checkbox angeklickt wird (in diesem Fall die Methode 'onCheckBox1'). Wenn die vierte Zeile nicht mit dem Python-Kommentarzeichen ('#') beginnen würde, würde sie ausgeführt werden und die Checkbox als aktiviert markieren. Die fünfte Zeile schließlich verschiebt die Checkbox an ihre Position.
# radio buttons
self.radioButton1 = QtGui.QRadioButton("random string one",self)
self.radioButton1.clicked.connect(self.onRadioButton1)
self.radioButton1.move(210,60)
#
self.radioButton2 = QtGui.QRadioButton("owt gnirts modnar",self)
self.radioButton2.clicked.connect(self.onRadioButton2)
self.radioButton2.move(210,80)
Die Erstellung der Radiobuttons ähnelt stark der Erstellung von Checkboxen. Der einzige Unterschied besteht darin, dass bei Radiobuttons jeweils nur einer aktiviert sein kann.
# set up lists for pop-ups
self.popupItems1 = ("pizza","apples","candy","cake","potatoes")
# set up pop-up menu
self.popup1 = QtGui.QComboBox(self)
self.popup1.addItems(self.popupItems1)
self.popup1.setCurrentIndex(self.popupItems1.index("candy"))
self.popup1.activated[str].connect(self.onPopup1)
self.popup1.move(210, 115)
In Zeile 2 wird eine Liste mit den Auswahlmöglichkeiten des Benutzers erstellt. Eine Alternative besteht darin, ein Wörterbuch zu erstellen, aber nur die Schlüssel für die Liste der Menüoptionen zu verwenden. In Zeile 4 wird das Popup-Menü (in PySide als ComboBox bezeichnet) erstellt, die Benutzeroptionen werden in Zeile 5 hinzugefügt.
Nebenbei bemerkt, wenn das Wörterbuch verwendet worden wäre, würden die Zeilen wie folgt erscheinen:
self.popupItems1 = OrderedDict([("2","widget"),("pink","foobar"),("4","galopsis")])
self.popup1.addItems(self.popupItems1.keys())
Zurück zum Hauptcodebeispiel für diesen Abschnitt: In Zeile 6 wird die Standardauswahl festgelegt. Diese Zeile kann weggelassen werden, und der Wert der Standardauswahl könnte (erneut, falls zutreffend) in das entsprechende Label geladen werden. Und schließlich erfolgt in Zeile 8 die Verschiebung an die Position.
# toggle visibility button
pushButton1 = QtGui.QPushButton('Toggle visibility', self)
pushButton1.clicked.connect(self.onPushButton1)
pushButton1.setAutoDefault(False)
pushButton1.move(210, 165)
Der Button wird in Zeile 2 mit seinem Namen erstellt, der Handler für sein Signal beim Klicken wird in Zeile 3 angegeben. Zeile 4 verhindert, dass der Button zum 'Standard-Button' wird – also zu dem Button, der angeklickt wird, wenn der Benutzer einfach die Return-Taste drückt. Und ein Move to Position rundet das Code-Segment ab.
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 280)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 280)
Beide Schaltflächen werden mit einem Namen (der als Beschriftung angezeigt wird) erstellt, der mit einer Methode verknüpft ist, die beim Klicken ausgeführt wird, und an die richtige Position verschoben. Die einzige Ausnahme ist Zeile 4, in der die Schaltfläche 'Abbrechen' als Standardschaltfläche festgelegt ist. Das bedeutet, dass sie "angeklickt" wird, wenn der Benutzer die Taste Return drückt.
# text input field
self.textInput = QtGui.QLineEdit(self)
self.textInput.setText("cats & dogs")
self.textInput.setFixedWidth(190)
self.textInput.move(20, 220)
Das QLineEdit-Widget ist wahrscheinlich das am häufigsten verwendete Widget für die Texteingabe durch den Benutzer. In diesem Beispiel wird im folgenden Codeabschnitt ein Kontextmenü für die Bedienung dieses Widgets eingerichtet. Dieser Codeabschnitt erstellt (Zeile 2) das Widget, legt einen Anfangswert fest (Zeile 3), legt die Breite des Feldes fest (Zeile 4) und verschiebt das Widget an die richtige Stelle (Zeile 5).
# QuantitySpinBox
from FreeCAD import Units
ui = FreeCADGui.UiLoader()
quantityInput = ui.createWidget("Gui::QuantitySpinBox")
self.quantityInput.setProperty( 'minimum', 0.0)
potential = 2.87
unit = "V"
# only set the value
self.quantityInput.setProperty('rawValue', potential )
# set quantity (value + unit)
quantity = Units.Quantity("{} {}".format(potential , unit))
self.quantityInput.setProperty('value', quantity)
# read value from the spinbox
quantity = self.quantityInput.property('value')
Das Widget Gui::QuantitySpinBox ist eine FreeCAD-Spezifikation, die zur Anzeige und Bearbeitung von Werten zusammen mit ihren Einheiten entwickelt wurde. Es ist von der Qt-Klasse QAbstractSpinBox abgeleitet. Alle Eigenschaften findet man in der Liste in der Quellcode-Datei QuantitySpinBox.h.
# set contextual menu options for text editing widget
# set text field to some dogerel
popMenuAction1 = QtGui.QAction(self)
popMenuAction1.setText("load some text")
popMenuAction1.triggered.connect(self.onPopMenuAction1)
# make text uppercase
popMenuAction2 = QtGui.QAction(self)
popMenuAction2.setText("uppercase")
popMenuAction2.triggered.connect(self.onPopMenuAction2)
# menu dividers
popMenuDivider = QtGui.QAction(self)
popMenuDivider.setText('---------')
popMenuDivider.triggered.connect(self.onPopMenuDivider)
# remove all text
popMenuAction3 = QtGui.QAction(self)
popMenuAction3.setText("clear")
popMenuAction3.triggered.connect(self.onPopMenuAction3)
# define menu and add options
self.textInput.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.textInput.addAction(popMenuAction1)
self.textInput.addAction(popMenuAction2)
self.textInput.addAction(popMenuDivider)
self.textInput.addAction(popMenuAction3)
Dieser Code enthält zahlreiche Wiederholungen, da dieselbe Aktion mit unterschiedlichen Werten ausgeführt wird – dies ist einer der Gründe, warum GUI-Code so lang ist (unabhängig vom System). Zunächst wird eine QAction erstellt – dabei handelt es sich um eine Paarung (oder Verknüpfung) des Textes, den der Benutzer als auswählbare Option sieht, mit der Methode, die ausgeführt wird, wenn er diese Option auswählt. Im Grunde genommen ist es eine Paarung einer Benutzerauswahl mit einem Stück Code. Zeile 3 erstellt sie, Zeile 4 definiert die Benutzeroption (so wie sie angezeigt wird) und Zeile 5 gibt an, welcher Python-Code ausgeführt wird.
In Zeile 19 (die Zeile mit "self.textInput.setContextMenuPolicy") wird ein ActionsContextMenu erstellt, das alle separaten QAction-Verknüpfungen zwischen der Benutzerauswahl und dem auszuführenden Code enthält. Jedes Widget kann nur ein einziges Kontextmenü haben (d. h. das Menü, das mit dem Rechtsklick verbunden ist), daher definiert Zeile 19 dieses Menü. Die folgenden 4 Zeilen fügen die zu Beginn dieses Codeabschnitts erstellten Verknüpfungen hinzu. Die Reihenfolge ist hier wichtig, da der Benutzer die Menüoptionen in der Reihenfolge sieht, in der sie hinzugefügt wurden. Beachten Sie, dass die dritte Menüoption eigentlich nichts bedeutet, ihr Code ist null, aber sie dient dazu, zwei Gruppen von Optionen im Kontextmenü zu trennen.
# numeric input field
self.numericInput = QtGui.QLineEdit(self)
self.numericInput.setInputMask("999")
self.numericInput.setText("000")
self.numericInput.setFixedWidth(50)
self.numericInput.move(250, 220)
Die Erstellung des Feldes für die numerische Eingabe folgt tatsächlich der zuvor beschriebenen Erstellung für die Texteingabe. Tatsächlich ist der Code bis auf die dritte und vierte Zeile identisch. Die dritte Zeile legt die von PySide definierte Maske fest, die in diesem Fall bis zu drei Ziffern (einschließlich 0) zulässt. Eine vollständige Liste der InputMask-Codes findet man unter QLineEdit InputMask.
# now make the window visible
self.show()
Es gibt nur eine Zeile, die dafür sorgt, dass die GUI nach der Einrichtung angezeigt wird.
Wir kommen nun zum operativen Teil der GUI-Definition, also dem Code, der als Reaktion auf Benutzerinteraktionen mit der GUI ausgeführt wird. Die Reihenfolge der Anweisungsgruppen ist nicht besonders relevant – mit der Einschränkung, dass etwas deklariert werden muss, bevor darauf verwiesen werden kann. Manche Leute fassen alle Handler eines bestimmten Typs (z. B. Handler für Schaltflächen) in einer Gruppe zusammen, andere listen die Handler alphabetisch auf. Bei bestimmten Anwendungen kann es aus praktischen Gründen sinnvoll sein, alle Handler, die sich auf einen bestimmten Aspekt beziehen, zusammenzufassen.
Die Handler weisen eine hohe Ähnlichkeit auf. Die meisten erhalten keinen Parameter, da die Tatsache, dass sie ausgeführt werden, der einzige Parameter (oder das einzige Signal) ist, den sie erhalten. Andere wie "onPopup1" und "mousePressEvent" akzeptieren einen Parameter.
Es muss eine Eins-zu-Eins-Entsprechung zwischen den im deklarativen Abschnitt angegebenen Handlern und dem im operativen Abschnitt deklarierten Handler bestehen. Es können zusätzliche Handler deklariert sein, die nie aufgerufen werden, aber es dürfen keine fehlen.
In diesem Codebeispiel behandeln generische Handler die folgenden Ereignisse:
Die allgemeine Form für die Handler lautet:
def handlerName(self):
lineOfCode1
lineOfCode2
Die erste Zeile enthält das Schlüsselwort "def", gefolgt vom Namen des Handlers. Der Name des Handlers muss genau mit dem Namen aus dem vorherigen deklarativen Abschnitt übereinstimmen. Der Parameter "self" ist ebenso wie die umschließenden Klammern und das abschließende Doppelpunktzeichen Teil der Standardsyntax. Nach der ersten Zeile gibt es keine Anforderungen an den folgenden Code, dieser ist rein anwendungsspezifisch.
def onPopup1(self, selectedText):
Der Popup-Menü-Handler entspricht dem generischen Handler, mit der Ausnahme, dass ein zweiter Parameter, der vom Benutzer ausgewählte Text, übergeben wird. Es muss beachtet werden, dass alles Text aus dem Popup-Menü ist und selbst wenn der Benutzer die Zahl 3 ausgewählt hat, diese als Zeichenfolge "3" übergeben wird.
def mousePressEvent(self, event):
# print mouse position, X & Y
print("X = ", event.pos().x())
print("Y = ", event.pos().y())
#
if event.button() == QtCore.Qt.LeftButton:
print("left mouse button")
if self.label1.underMouse():
print("over the text '"+self.label1.text()+"'")
Der Mouse Event-Handler entspricht dem generischen Handler, mit der Ausnahme, dass ein zweiter Parameter, das Mausereignis (z. B. Linksklick, Rechtsklick) des Benutzers, übergeben wird . Der Name des Handlers, "mousePressEvent", ist reserviert. Wenn er geändert wird, empfängt der Handler keine Ereignisse mehr von Mausklicks.
Die X- und Y-Koordinaten des Mausklicks werden durch die Referenz "event.pos().x()" und "event.pos().y()" angegeben. Die Konstanten "QtCore.Qt.LeftButton" und "QtCore.Qt.RightButton" werden verwendet, um zu bestimmen, welche Maustaste gedrückt wurde.
Ein Verweis auf ein Widget kann in der Form "self.widgetName.underMouse()" erfolgen, wodurch true oder false zurückgegeben wird, je nachdem, ob sich der Mauszeiger über dem Widget "widgetName" befindet. Obwohl im gleichen Codeausschnitt dargestellt, ist der Handler "underMouse()" nicht an den Handler "mousePressEvent" gebunden und kann jederzeit verwendet werden.
Der Großteil des Codes befindet sich in der GUI-Klassendefinition, in der Hauptprozedur gibt es nicht viel.
# Constant definitions
global userCancelled, userOK
userCancelled = "Cancelled"
userOK = "OK"
Die Zeilen 2, 3 und 4 befassen sich mit der Koordinierung des Status der Benutzerinteraktion mit der GUI – z. B. „Abgebrochen“, „OK“ oder einem anderen von der Anwendung definierten Status. Die zuvor genannten Handler-Routinen „onCancel“ und „onOk“ legen ebenfalls diese Stati fest.
form = ExampleGuiClass()
form.exec_()
if form.result==userCancelled:
pass # steps to handle user clicking Cancel
if form.result==userOK:
# steps to handle user clicking OK
localVariable1 = form.label1.text()
localVariable2 = form.label2.text()
localVariable3 = form.label3.text()
localVariable4 = form.label4.text()
Die Zeilen 1 und 2 zeigen die Methode zum Aufrufen der GUI. Es kann mehrere GUI-Definitionen für ein Programm geben, und die GUI muss nicht als erstes Element in der Python-Datei aufgerufen werden, sondern kann an beliebiger Stelle aufgerufen werden. Der Name der GUI-Klasse wird in Zeile 1 angegeben (in diesem Fall "ExampleGuiClass"), aber die restlichen beiden Zeilen müssen wörtlich kopiert werden.
Die Zeilen 4 und 6 verwenden das Ergebnisfeld, um die entsprechende Aktion zu bestimmen. Die letzten 4 Zeilen zeigen lediglich das Kopieren der Daten im GUI-Objekt in lokale Variablen der ausführenden Hauptprozedur.
Dies ist das vollständige Codebeispiel (entwickelt auf FreeCAD v0.14):
# import statements
from PySide import QtGui, QtCore
# UI Class definitions
class ExampleModalGuiClass(QtGui.QDialog):
""""""
def __init__(self):
super(ExampleModalGuiClass, self).__init__()
self.initUI()
def initUI(self):
self.result = userCancelled
# create our window
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 400, 350)
self.setWindowTitle("Our Example Modal Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
# create some Labels
self.label1 = QtGui.QLabel(" ", self)
self.label1.setFont('Courier') # set to a non-proportional font
self.label1.move(20, 20)
self.label2 = QtGui.QLabel("sample string number two", self)
self.label2.move(20, 70)
self.label3 = QtGui.QLabel(" ", self)
self.label3.setFont('Courier') # set to a non-proportional font
self.label3.move(20, 120)
self.label4 = QtGui.QLabel("can you see this?", self)
self.label4.move(20, 170)
# checkboxes
self.checkbox1 = QtGui.QCheckBox("Left side", self)
self.checkbox1.clicked.connect(self.onCheckbox1)
#self.checkbox1.toggle() # will set an initial value if executed
self.checkbox1.move(210,10)
#
self.checkbox2 = QtGui.QCheckBox("Right side", self)
self.checkbox2.clicked.connect(self.onCheckbox2)
self.checkbox2.move(210,30)
# radio buttons
self.radioButton1 = QtGui.QRadioButton("random string one",self)
self.radioButton1.clicked.connect(self.onRadioButton1)
self.radioButton1.move(210,60)
#
self.radioButton2 = QtGui.QRadioButton("owt gnirts modnar",self)
self.radioButton2.clicked.connect(self.onRadioButton2)
self.radioButton2.move(210,80)
# set up lists for pop-ups
self.popupItems1 = ("pizza","apples","candy","cake","potatoes")
# set up pop-up menu
self.popup1 = QtGui.QComboBox(self)
self.popup1.addItems(self.popupItems1)
self.popup1.setCurrentIndex(self.popupItems1.index("candy"))
self.popup1.activated[str].connect(self.onPopup1)
self.popup1.move(210, 115)
# toggle visibility button
pushButton1 = QtGui.QPushButton('Toggle visibility', self)
pushButton1.clicked.connect(self.onPushButton1)
pushButton1.setAutoDefault(False)
pushButton1.move(210, 165)
# text input field
self.textInput = QtGui.QLineEdit(self)
self.textInput.setText("cats & dogs")
self.textInput.setFixedWidth(190)
self.textInput.move(20, 220)
# set contextual menu options for text editing widget
# set text field to some dogerel
popMenuAction1 = QtGui.QAction(self)
popMenuAction1.setText("load some text")
popMenuAction1.triggered.connect(self.onPopMenuAction1)
# make text uppercase
popMenuAction2 = QtGui.QAction(self)
popMenuAction2.setText("uppercase")
popMenuAction2.triggered.connect(self.onPopMenuAction2)
# menu dividers
popMenuDivider = QtGui.QAction(self)
popMenuDivider.setText('---------')
popMenuDivider.triggered.connect(self.onPopMenuDivider)
# remove all text
popMenuAction3 = QtGui.QAction(self)
popMenuAction3.setText("clear")
popMenuAction3.triggered.connect(self.onPopMenuAction3)
# define menu and add options
self.textInput.setContextMenuPolicy(QtCore.Qt.ActionsContextMenu)
self.textInput.addAction(popMenuAction1)
self.textInput.addAction(popMenuAction2)
self.textInput.addAction(popMenuDivider)
self.textInput.addAction(popMenuAction3)
# numeric input field
self.numericInput = QtGui.QLineEdit(self)
self.numericInput.setInputMask("999")
self.numericInput.setText("000")
self.numericInput.setFixedWidth(50)
self.numericInput.move(250, 220)
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 280)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 280)
# now make the window visible
self.show()
#
def onCheckbox1(self):
text = self.label1.text()
if text[0]==' ':
self.label1.setText('left'+text[4:])
else:
self.label1.setText(' '+text[4:])
def onCheckbox2(self):
text = self.label1.text()
if text[-1]==' ':
self.label1.setText(text[:-5]+'right')
else:
self.label1.setText(text[:-5]+' ')
def onRadioButton1(self):
self.label2.setText(self.radioButton1.text())
def onRadioButton2(self):
self.label2.setText(self.radioButton2.text())
def onPopup1(self, selectedText):
if self.label3.text().isspace():
self.label3.setText(selectedText)
else:
self.label3.setText(self.label3.text()+","+selectedText)
def onPushButton1(self):
if self.label4.isVisible():
self.label4.hide()
else:
self.label4.show()
def onPopMenuAction1(self):
# load some text into field
self.textInput.setText("Lorem ipsum dolor sit amet")
def onPopMenuAction2(self):
# set text in field to uppercase
self.textInput.setText(self.textInput.text().upper())
def onPopMenuDivider(self):
# this option is the divider and is really there as a spacer on the menu list
# consequently it has no functional code to execute if user selects it
pass
def onPopMenuAction3(self):
# clear the text from the field
self.textInput.setText('')
def onCancel(self):
self.result = userCancelled
self.close()
def onOk(self):
self.result = userOK
self.close()
def mousePressEvent(self, event):
# print mouse position, X & Y
print("X = ", event.pos().x())
print("Y = ", event.pos().y())
#
if event.button() == QtCore.Qt.LeftButton:
print("left mouse button")
if self.label1.underMouse():
print("over the text '"+self.label1.text()+"'")
if self.label2.underMouse():
print("over the text '"+self.label2.text()+"'")
if self.label3.underMouse():
print("over the text '"+self.label3.text()+"'")
if self.label4.underMouse():
print("over the text '"+self.label4.text()+"'")
if self.textInput.underMouse():
print("over the text '"+self.textInput.text()+"'")
if event.button() == QtCore.Qt.RightButton:
print("right mouse button")
# Class definitions
# Function definitions
# Constant definitions
userCancelled = "Cancelled"
userOK = "OK"
# code ***********************************************************************************
form = ExampleModalGuiClass()
form.exec_()
if form.result==userCancelled:
pass # steps to handle user clicking Cancel
if form.result==userOK:
# steps to handle user clicking OK
localVariable1 = form.label1.text()
localVariable2 = form.label2.text()
localVariable3 = form.label3.text()
localVariable4 = form.label4.text()
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
#
Am besten kopiert man diesen Code in einen Editor oder eine FreeCAD-Makrodatei und probieren ihn dort aus.
Alle Widget-spezifischen Elemente aus dem vorherigen modalen Beispiel werden für die Verwendung in einem nicht-modalen Fenster übertragen. Der Hauptunterschied besteht darin, dass das nicht-modale Fenster den Benutzer nicht daran hindert, mit anderen Fenstern zu interagieren. Grundsätzlich ist ein nicht-modales Fenster ein Fenster, das geöffnet und so lange wie nötig geöffnet bleiben kann, ohne dass es Einschränkungen für andere Anwendungsfenster gibt. Es gibt eine kleine Anzahl von Code-Unterschieden zwischen den beiden, die hervorgehoben werden, daher ist dieses Code-Beispiel recht kurz. Alles, was mit dem vorherigen modalen Beispiel identisch ist, wird weggelassen, um diesen Überblick kurz zu halten. Dies ist der nichtmodale GUI-Bildschirm, den die PySide-Klasse generiert:
Die zwingend erforderliche Import-Anweisung
from PySide import QtGui, QtCore
Diese wird am besten am Anfang der Python-Datei eingesetzt.
class ExampleNonmodalGuiClass(QtGui.QMainWindow):
""""""
def __init__(self):
super(ExampleNonmodalGuiClass, self).__init__()
self.initUI()
def initUI(self):
Dieser Code sollte am besten wörtlich kopiert und geändert werden. Der Kern des Codes besteht darin, dass wir die Klasse QMainWindow von PySide unterteilen. Bei der Anpassung dieses Codes sollte man den Klassennamen "ExampleNonmodalGuiClass" ändern – darauf achten, ihn an beiden Stellen zu ändern (z. B. Zeile 1 und 4).
# create our window
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 400, 150)
self.setWindowTitle("Our Example Nonmodal Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setMouseTracking(True)
Offensichtlich unterscheiden sich unsere Fensterabmessungen und unser Titel. Der wichtigste Punkt ist die letzte Zeile, die PySide mitteilt, dass es Mauspositionsereignisse sofort nach ihrem Auftreten senden soll. Es muss beachtet werden, dass diese Ereignisse nicht gesendet werden, wenn sich die Maus über einem Widget wie einer Schaltfläche befindet, da das Widget die Ereignisse erfasst.
def mouseMoveEvent(self,event):
self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))
Dieser Handler empfängt das Ereignis einer Mausbewegung und zeigt die formatierte Form davon an. Testen, was passiert, wenn es sich über Widgets oder außerhalb des Fensters befindet.
form = ExampleNonmodalGuiClass()
Das Aufrufen des Fensters ist ein weiterer Unterschied zum vorherigen Beispiel. Dieses Mal ist nur eine Zeile erforderlich, um die GUI aufzurufen.
from PySide import QtGui, QtCore
# UI Class definitions
class ExampleNonmodalGuiClass(QtGui.QMainWindow):
""""""
def __init__(self):
super(ExampleNonmodalGuiClass, self).__init__()
self.initUI()
def initUI(self):
self.result = userCancelled
# create our window
# define window xLoc,yLoc,xDim,yDim
self.setGeometry( 250, 250, 400, 150)
self.setWindowTitle("Our Example Nonmodal Program Window")
self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint)
self.setMouseTracking(True)
# create Labels
self.label4 = QtGui.QLabel("can you see this?", self)
self.label4.move(20, 20)
self.label5 = QtGui.QLabel("Mouse position:", self)
self.label5.move(20, 70)
self.label6 = QtGui.QLabel(" ", self)
self.label6.move(135, 70)
# toggle visibility button
pushButton1 = QtGui.QPushButton('Toggle visibility', self)
pushButton1.clicked.connect(self.onPushButton1)
pushButton1.setMinimumWidth(150)
#pushButton1.setAutoDefault(False)
pushButton1.move(210, 20)
# cancel button
cancelButton = QtGui.QPushButton('Cancel', self)
cancelButton.clicked.connect(self.onCancel)
cancelButton.setAutoDefault(True)
cancelButton.move(150, 110)
# OK button
okButton = QtGui.QPushButton('OK', self)
okButton.clicked.connect(self.onOk)
okButton.move(260, 110)
# now make the window visible
self.show()
#
def onPushButton1(self):
if self.label4.isVisible():
self.label4.hide()
else:
self.label4.show()
def onCancel(self):
self.result = userCancelled
self.close()
def onOk(self):
self.result = userOK
self.close()
def mouseMoveEvent(self,event):
self.label6.setText("X: "+str(event.x()) + " Y: "+str(event.y()))
# Class definitions
# Function definitions
# Constant definitions
global userCancelled, userOK
userCancelled = "Cancelled"
userOK = "OK"
# code ***********************************************************************************
form = ExampleNonmodalGuiClass()
#
#OS: Mac OS X
#Word size: 64-bit
#Version: 0.14.3703 (Git)
#Branch: releases/FreeCAD-0-14
#Hash: c6edd47334a3e6f209e493773093db2b9b4f0e40
#Python version: 2.7.5
#Qt version: 4.8.6
#Coin version: 3.1.3
#SoQt version: 1.5.0
#OCC version: 6.7.0
Es gibt drei Konzepte für den Bildschirmplatz in einer GUI-Umgebung:
Innerhalb der Software werden alle in Pixeln gemessen. PySide verfügt über eine Funktion zur Messung in realen Einheiten, diese sind jedoch unzuverlässig, da die Hersteller keine Standards für Pixelgröße oder Seitenverhältnis haben.
Der Rahmen hat die Größe eines Fensters einschließlich seiner Seitenleisten, der oberen Leiste (möglicherweise mit einem Menü darin) und der unteren Leiste. Die Geometrie ist der Raum innerhalb des Rahmens und daher immer kleiner oder gleich dem Rahmen. Der Rahmen wiederum ist immer kleiner oder gleich der verfügbaren Bildschirmgröße.
# get screen dimensions (Available Screen Size)
screenWidth = QtGui.QDesktopWidget().screenGeometry().width()
screenHeight = QtGui.QDesktopWidget().screenGeometry().height()
# get dimensions for available space on screen
availableWidth = QtGui.QDesktopWidget().availableGeometry().width()
availableHeight = QtGui.QDesktopWidget().availableGeometry().height()
Im Allgemeinen sollte "availableHeight" um die Höhe der Menüleiste kleiner sein als "screenHeight". Diese vier Werte basieren auf der Hardwareumgebung und variieren von Computer zu Computer. Sie sind nicht von der Größe des Anwendungsfensters abhängig.
(Seit Python 3.9 erscheint bei Ausführung des obigen Codes folgende Warnung: DeprecationWarning: QDesktopWidget.screenGeometry(int screen) const ist veraltet. Ab Python 3.10 scheint ein Ersatz erforderlich zu sein.)
# set up a variable to hold the Main Window to save some typing...
mainWin = FreeCAD.Gui.getMainWindow()
mainWin.showFullScreen() # no menu bars, every last pixel is given over to FreeCAD
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()
mainWin.showMaximized() # show maximised within the screen, window edges and the menu bar will be displayed
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()
mainWin.showNormal() # show at the last non-maximised or non-minimised size (and location)
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()
mainWin.setGeometry(50, 50, 800, 800) # specifically set FreeCAD main window's size and location, this will become the new setting for 'showNormal()'
mainWin.showMinimized() # FreeCAD will disappear from view after this command...
mainWin.geometry()
mainWin.frameSize()
mainWin.frameGeometry()
Dieselben Befehle können in einem vom Benutzer erstellten Fenster ausgeführt werden, die Syntax ändert sich dabei nicht.